#pragma once

#include <iostream>
#include <string>
#include <cstring>

#define SEP "\r\n"
#define SEPLEN strlen(SEP)

using namespace std;

string EnHeader(const string &inbuffer) // 添加报头
{
    int len = inbuffer.size();
    string out = to_string(len);
    out += SEP;
    out += inbuffer;
    out += SEP; // 报文结尾也要用分隔符来与其他报文做分割
    return out;
}
string DelHeader(const string &inbuffer) // 去报头
{
    // 去报头的前提我们已经拿到一个完整的请求了,所以我们认为传过来的 inbuffer 是一个完整的报文
    // 报文格式(没有-)：length-SEP-有效载荷-SEP ,那么有效载荷就是两个SEP之间的数据
    // 当然，这样一来我们的协议其实要规定好要传输的数据不能包含SEP这样的特殊字段
    string out;
    int left = inbuffer.find(SEP);                               // 从前往后找第一个SEP,返回的是第一个SEP的其实的位置
    int right = inbuffer.rfind(SEP);                             // 从后往前找第二个SEP,返回的是第二个SEP的起始位置
    out = inbuffer.substr(left + SEPLEN, right - left - SEPLEN); // right-left是两个SEP的起始位置之间的字符个数，所以还要减去SEPLEN
    return out;
}

void ReadOneText(int sock, string &outbuffer, string &inbuffer)
{
    while (1)
    {
        char buffer[1024];
        bzero(buffer, sizeof buffer);
        int n = read(sock, buffer, sizeof buffer);
        if (n > 0)
        {
            buffer[n] = 0;      // 说明从内核缓冲区中读到了新的数据，那么这时候就可以再判断一下现在能不能读到一个完整报文
            inbuffer += buffer; // 读取到的数据加到缓冲区中
            // cout<<"buffer:"<<buffer<<endl;
            // 先提取报文的有效载荷长度
            int n = inbuffer.find(SEP); // 找到第一个SEP，那么前面的就是我们的第一个报文的报头
            if (n == string::npos)
                continue; // 说明不够，那么就等读取新的数据到缓冲区
            // 说明能读到一个报文的报头
            string lenstring = inbuffer.substr(0, n);
            // cout<<"lenstring:"<<lenstring<<endl;
            int len = atoi(lenstring.c_str());
            // 然后判断缓冲区的长度能不能放下第一个完整的报文
            int TotalLen = len+2*SEPLEN + lenstring.size();
            // cout<<"TotalLen:"<<TotalLen<<" -- inbuffer.size:"<<inbuffer.size()<<endl;
            if (TotalLen > inbuffer.size()) // 说明第一个报文不完整,等读到新的数据再来判断
                continue;
            // 到这里说明缓冲区中有一个完整的报文了
            string Text = inbuffer.substr(0, TotalLen); // 提取完整报文
            // cout<<"Text"<<Text<<endl;
            // 去报头
            outbuffer = DelHeader(Text);
            // 读走之后要将该报文从缓冲区中移除
            // cout<<"outbuffer:"<<outbuffer<<endl;
            inbuffer.erase(inbuffer.begin(), inbuffer.begin() + TotalLen);
            // cout<<"一次读取完之后 inbuffer:"<<inbuffer<<endl;
            return ;
        }
        else if (n == 0) // 没有新内容
        {
            continue;
        }
        else // n<0说明读取出错
        {
            exit(READ_ERR);
        }
    }
}




enum ExitCode
{
    NORMAL, // 0表示正常
    DIV_ZERO,
    MOD_ZERO,
    UNKNOWN_OP
};

void protocolerr(ExitCode e)
{
    if(e==DIV_ZERO) cout<<"DIV_ZERO"<<endl;
    else if(e==MOD_ZERO) cout<<"MOD_ZERO"<<endl;
    else cout<<"UNKNOWN_OP"<<endl;
}

class Response
{
public:
    string Serialize()
    {
        // _exitcode\r\n_result
        int exitcode = 0;
        if(_exitcode == DIV_ZERO) exitcode = 1;
        else if(_exitcode == MOD_ZERO) exitcode = 2;
        else if(_exitcode==UNKNOWN_OP)exitcode = 3;

        string out = to_string(exitcode);
        out += SEP;
        out += to_string(_result);
        return out;
    }

    void Deserialize(string inbuffer)
    {
        int sep = inbuffer.find(SEP);
        int code = stoi(inbuffer.substr(0, sep));
        switch (code)
        {
        case 0:
        {
            _exitcode = NORMAL;
            break;
        }
        case 1:
        {
            _exitcode = DIV_ZERO;
            break;
        }
        case 2:
        {
            _exitcode = MOD_ZERO;
            break;
        }
        case 3:
        {
            _exitcode = UNKNOWN_OP;
            break;
        }
        default: break;
        }
        _result = stoi(inbuffer.substr(sep + SEPLEN));
    }

public:
    ExitCode _exitcode;
    int _result;
};

class Request
{
public:
    string  Serialize()
    {
        //_x\r\n_op\r\n_y
        string out;
        out += to_string(_x);
        out += SEP;
        out += _op;
        out += SEP;
        out += to_string(_y);
        return out;
    }

    void Deserialize(string inbuffer)
    {
        //_x\r\n_op\r\n_y
        int sep1 = inbuffer.find(SEP);
        _x = stoi(inbuffer.substr(0, sep1)); //_x
        _op = inbuffer[sep1 + SEPLEN];       //_op
        int sep2 = inbuffer.rfind(SEP);
        _y = stoi(inbuffer.substr(sep2 + SEPLEN)); //_y
    }

public:
    int _x;
    char _op;
    int _y;
};
